from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, update
from app.db.postgres import get_session
from app.models import LLMProvider, LLMModel, LLMSecretVault
from app.schemas.llm_config import (
    LLMProviderCreate,
    LLMProviderUpdate,
    LLMProviderRead,
    LLMModelCreate,
    LLMModelUpdate,
    LLMModelRead,
    LLMSecretCreate,
    LLMSecretUpdate,
    LLMSecretRead,
)
import os
import base64
import hashlib
from fastapi import status
from app.config import settings
try:
    from cryptography.hazmat.primitives.ciphers.aead import AESGCM
except Exception:
    AESGCM = None


router = APIRouter(prefix="/llm", tags=["llm-config"])


# Providers
@router.get("/providers", response_model=list[LLMProviderRead])
async def list_providers(session: AsyncSession = Depends(get_session)):
    res = await session.execute(select(LLMProvider))
    return [LLMProviderRead.model_validate(x) for x in res.scalars().all()]


@router.post("/providers", response_model=LLMProviderRead)
async def create_provider(payload: LLMProviderCreate, session: AsyncSession = Depends(get_session)):
    obj = LLMProvider(**payload.model_dump())
    session.add(obj)
    await session.flush()
    await session.refresh(obj)
    await session.commit()
    return LLMProviderRead.model_validate(obj)


@router.put("/providers/{provider_id}", response_model=LLMProviderRead)
async def update_provider(provider_id: str, payload: LLMProviderUpdate, session: AsyncSession = Depends(get_session)):
    obj = await session.get(LLMProvider, provider_id)
    if not obj:
        raise HTTPException(status_code=404, detail="provider_not_found")
    for k, v in payload.model_dump(exclude_unset=True).items():
        setattr(obj, k, v)
    await session.flush()
    await session.refresh(obj)
    await session.commit()
    return LLMProviderRead.model_validate(obj)


@router.delete("/providers/{provider_id}")
async def delete_provider(provider_id: str, session: AsyncSession = Depends(get_session)):
    obj = await session.get(LLMProvider, provider_id)
    if not obj:
        raise HTTPException(status_code=404, detail="provider_not_found")
    await session.delete(obj)
    await session.commit()
    return {"ok": True}


# Models
@router.get("/models", response_model=list[LLMModelRead])
async def list_models(session: AsyncSession = Depends(get_session)):
    res = await session.execute(select(LLMModel))
    return [LLMModelRead.model_validate(x) for x in res.scalars().all()]


@router.post("/models", response_model=LLMModelRead)
async def create_model(payload: LLMModelCreate, session: AsyncSession = Depends(get_session)):
    obj = LLMModel(**payload.model_dump())
    session.add(obj)
    await session.flush()
    await session.refresh(obj)
    await session.commit()
    return LLMModelRead.model_validate(obj)


@router.put("/models/{model_id}", response_model=LLMModelRead)
async def update_model(model_id: str, payload: LLMModelUpdate, session: AsyncSession = Depends(get_session)):
    obj = await session.get(LLMModel, model_id)
    if not obj:
        raise HTTPException(status_code=404, detail="model_not_found")
    for k, v in payload.model_dump(exclude_unset=True).items():
        setattr(obj, k, v)
    await session.flush()
    await session.refresh(obj)
    await session.commit()
    return LLMModelRead.model_validate(obj)


@router.delete("/models/{model_id}")
async def delete_model(model_id: str, session: AsyncSession = Depends(get_session)):
    obj = await session.get(LLMModel, model_id)
    if not obj:
        raise HTTPException(status_code=404, detail="model_not_found")
    await session.delete(obj)
    await session.commit()
    return {"ok": True}


# Secrets (do not return cipher_text)
@router.get("/secrets", response_model=list[LLMSecretRead])
async def list_secrets(session: AsyncSession = Depends(get_session)):
    res = await session.execute(select(LLMSecretVault))
    secrets = res.scalars().all()
    return [
        LLMSecretRead.model_validate({
            "id": x.id,
            "provider_id": x.provider_id,
            "name": x.name,
            "cipher_algo": x.cipher_algo,
            "key_id": x.key_id,
            "rotation_version": x.rotation_version,
            "enabled": x.enabled,
            "created_at": x.created_at,
            "updated_at": x.updated_at,
        }) for x in secrets
    ]


@router.post("/secrets", response_model=LLMSecretRead)
async def create_secret(payload: LLMSecretCreate, session: AsyncSession = Depends(get_session)):
    if AESGCM is None:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="crypto_not_available")
    key_source = settings.LLM_SECRET_KEY or settings.JWT_SECRET
    key = hashlib.sha256(key_source.encode()).digest()
    aes = AESGCM(key)
    iv = os.urandom(12)
    ct = aes.encrypt(iv, payload.secret_value.encode(), None)
    ct_bytes = ct[:-16]
    tag_bytes = ct[-16:]
    obj = LLMSecretVault(
        provider_id=payload.provider_id,
        name=payload.name,
        cipher_algo="aes-256-gcm",
        cipher_text=ct_bytes,
        iv=iv,
        auth_tag=tag_bytes,
        key_id="app-default",
        rotation_version=1,
        enabled=payload.enabled,
    )
    session.add(obj)
    await session.flush()
    await session.refresh(obj)
    await session.commit()
    return LLMSecretRead.model_validate(obj)


@router.put("/secrets/{secret_id}", response_model=LLMSecretRead)
async def update_secret(secret_id: str, payload: LLMSecretUpdate, session: AsyncSession = Depends(get_session)):
    obj = await session.get(LLMSecretVault, secret_id)
    if not obj:
        raise HTTPException(status_code=404, detail="secret_not_found")
    for k, v in payload.model_dump(exclude_unset=True).items():
        if k in {"provider_id","name","enabled"}:
            setattr(obj, k, v)
    if payload.secret_value is not None:
        if AESGCM is None:
            raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="crypto_not_available")
        key_source = settings.LLM_SECRET_KEY or settings.JWT_SECRET
        key = hashlib.sha256(key_source.encode()).digest()
        aes = AESGCM(key)
        iv = os.urandom(12)
        ct = aes.encrypt(iv, payload.secret_value.encode(), None)
        ct_bytes = ct[:-16]
        tag_bytes = ct[-16:]
        obj.cipher_algo = "aes-256-gcm"
        obj.cipher_text = ct_bytes
        obj.iv = iv
        obj.auth_tag = tag_bytes
        obj.key_id = "app-default"
        obj.rotation_version = (obj.rotation_version or 1) + 1
    await session.flush()
    await session.refresh(obj)
    await session.commit()
    return LLMSecretRead.model_validate(obj)


@router.delete("/secrets/{secret_id}")
async def delete_secret(secret_id: str, session: AsyncSession = Depends(get_session)):
    obj = await session.get(LLMSecretVault, secret_id)
    if not obj:
        raise HTTPException(status_code=404, detail="secret_not_found")
    await session.delete(obj)
    await session.commit()
    return {"ok": True}